package com.ruoyi.project.wx.api;


import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bootdo.*;
import com.ruoyi.project.wx.domain.MemberUserDO;
import com.ruoyi.project.wx.domain.OrderOrderDO;
import com.ruoyi.project.wx.domain.SetWechatDO;
import com.ruoyi.project.wx.service.MemberUserService;
import com.ruoyi.project.wx.service.OrderOrderService;
import com.ruoyi.project.wx.service.SetWechatService;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.velocity.texen.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.util.*;

/**
 * Created by HP on 2019/7/13.
 */
@Controller
@RequestMapping("/api/wxPay")
public class ApiWeChatPayController {

    @Autowired
    private SetWechatService setWechatService;
    @Autowired
    private OrderOrderService orderOrderService;
    @Autowired
    private MemberUserService memberUserService;


    private final Logger log = LoggerFactory.getLogger(this.getClass());
    private  int socketTimeout = 10000;// 连接超时时间，默认10秒
    private  int connectTimeout = 30000;// 传输超时时间，默认30秒
    private  static RequestConfig requestConfig;// 请求器的配置
    private  static CloseableHttpClient httpClient;// HTTP请求器



    /**
     * 微信支付接口
     *
     * @param params
     * @param req
     * @return
     */
    @RequestMapping("/pay")
    @ResponseBody
    public R pay(@RequestBody Map<String, Object> params, HttpServletRequest req) {
        try {
//            log.debug("map::" + Json.toJson(map));
            if (!params.containsKey("orderId") || params.get("orderId").toString().isEmpty()
                    ||!params.containsKey("userId") || params.get("userId").toString().isEmpty()
                    ) {
                return R.error(100, "参数错误");
            }
            String orderId = params.get("orderId").toString();
            String userId = params.get("userId").toString();
            OrderOrderDO order = orderOrderService.get(orderId);
            if (order == null) {
                return R.error(101, "订单异常");
            }
            MemberUserDO userDO = memberUserService.get(userId);
            if (userDO == null) {
                return R.error(103, "用户异常");
            }
            SetWechatDO setWechatDO = setWechatService.list(new HashMap<String, Object>()).get(0);
            SortedMap<String, String> signMap = new TreeMap<>();


            signMap.put("body", "商品"); //商品描述  必

            //signMap.put("appId", goods.getIntro()); //应用ID  必
            //signMap.put("mch_id", goods.getGoodsName()); //商户号  必
            //signMap.put("sign_type", "MD5"); //签名类型默认MD5
            //signMap.put("notify_url", order.getTotalAmount()); //通知地址 回调url  必
            //signMap.put("trade_type", "APP"); //交易类型  APP 必
            //signMap.put("time_start", order.getTotalAmount()); //交易开始时间  必
            //signMap.put("time_expire", order.getTotalAmount()); //交易结束时间    大于5分钟
            //signMap.put("nonce_str", order.getOrderSn()); //随机字符串  必

//            signMap.put("body", goods.getGoodsName()); //商品描述  必
            signMap.put("attach", "商品"); //商品详情  非
            signMap.put("openid", userDO.getWechatOpenId()); //小程序支付必填，其他非填
            signMap.put("out_trade_no", order.getOrderSn()); //商户订单号  必
            /*float sessionmoney = Float.parseFloat(order.getPayment().toString());
            String finalmoney = String.format("%.2f", sessionmoney);
            finalmoney = finalmoney.replace(".", "");
            String newStr = finalmoney.replaceFirst("^0*", "");*/
            int total_fee  = Math.round(Float.valueOf(order.getPayment().multiply(new BigDecimal(100)).toString()));
            signMap.put("total_fee", String.valueOf(total_fee)); //总金额  必    /分
            //.put("total_fee", "1");
            signMap.put("spbill_create_ip", StringUtils.getRemoteAddr(req)); //终端ip  用户端ip  必

            Map<String,Object> map = WeChatPayUtils.getOrderSignature(signMap,setWechatDO);

//            log.debug("weixin sign=="+wxorder);

//            logService.async(Mp_log.c(map.getString("appId"), map.getString("appSecret"), "", "user.weixinPay", "微信支付", "微信支付"));
            return R.ok().put("data", map);
        } catch (Exception e) {
            return R.error("微信支付失败!");
        }
    }


    /*微信支付回调*/
    @ResponseBody
    @RequestMapping("/weixinpayNotify")
    public Object weixinpayNotify(HttpServletRequest req, HttpServletResponse res) {
        try {
            InputStream inStream = req.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            System.out.println("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~");
            outSteam.close();
            inStream.close();
            String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
            Map<Object, Object> mapFromXml = PayCommonUtil.doXMLParse(result);
            log.error("mapFromXml::" + mapFromXml.toString());
            SortedMap<String ,String > sortedMap = new TreeMap();
            log.error("map001::");
            if (mapFromXml.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
                log.error("map002::");
                OrderOrderDO order = orderOrderService.getByOrder(mapFromXml.get("out_trade_no").toString());
                if (order.getStatus() == 1) {
                    log.error("map003::" + order.toString());
                    if (verifyWeixinNotify(mapFromXml)) {
                        log.error("map004::开始订单处理");
                        //订单处理
                        order.setStatus(3);
                        order.setPaytype(0);
                        order.setPayAt(new Date());
                        log.error("map005::");
                        orderOrderService.update(order);
                        log.error("微信支付成功::");
                        // 告诉微信服务器，我收到信息了，不要在调用回调action了
                        sortedMap.put("return_code","SUCCESS");
                        sortedMap.put("return_msg","OK");
                        res.getWriter().write(parseXML(sortedMap));

                    }else{
                        log.error("map011::");
                        sortedMap.put("return_code","FAIL");
                        sortedMap.put("return_msg","签名失败");
                        res.getWriter().write(parseXML(sortedMap));
                    }
                    log.error("map012::");
                } else {
                    log.error("map013::订单已经修改过了");
                }
            }
            log.error("map012::");
            return "success";
        } catch (Exception e) {
            return "failure";
        }
    }

    public boolean verifyWeixinNotify(Map<Object, Object> map) {
        SetWechatDO setWechatDO = setWechatService.list(new HashMap<String, Object>()).get(0);
        SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
        String sign = (String) map.get("sign");
        for (Object keyValue : map.keySet()) {
            if(!keyValue.toString().equals("sign")){
                parameterMap.put(keyValue.toString(), map.get(keyValue));
            }
        }
        String createSign = PayCommonUtil.createSign("UTF-8", parameterMap,setWechatDO.getKey());
        if(createSign.equals(sign)){
            return true;
        }else{
            return false;
        }

    }

    //输出XML
    public String parseXML(Map map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>\n");
        Set es = map.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(null != v && !"".equals(v) && !"appkey".equals(k)) {

                sb.append("<" + k +">" + v + "</" + k + ">\n");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    //具体的调用微信的退款接口
    public R refund(String orderId, OrderOrderService orderOrderService2, SetWechatService setWechatService2) throws Exception{
        Map <String,String> data = new HashMap<String,String>();
        try {
            OrderOrderDO order = orderOrderService2.get(orderId);
            SetWechatDO setWechatDO = setWechatService2.list(new HashMap<String, Object>()).get(0);
            //退款到用户微信
            String nonce_str = MD5Utils.getRandomStringByLength(32);
            //data.put("userId", String.valueOf(userecord.getUserId()));
            data.put("appid", setWechatDO.getAppId());
            data.put("mch_id", setWechatDO.getMchId());
            data.put("nonce_str", nonce_str);
            data.put("sign_type", "MD5");
            data.put("out_trade_no", order.getOrderSn());//商户订单号
            data.put("out_refund_no", UuidUtil.get32UUID());//商户退款单号
            int total_fee  = Math.round(Float.valueOf(order.getPayment().multiply(new BigDecimal(100)).toString()));
            data.put("total_fee",String.valueOf(Math.round(total_fee)));//支付金额，微信支付提交的金额是不能带小数点的，且是以分为单位,这边需要转成字符串类型，否则后面的签名会失败
            data.put("refund_fee", String.valueOf(Math.round(total_fee)));//退款总金额,订单总金额,单位为分,只能为整数
            /*data.put("total_fee","1");//支付金额，微信支付提交的金额是不能带小数点的，且是以分为单位,这边需要转成字符串类型，否则后面的签名会失败
            data.put("refund_fee", "1");//退款总金额,订单总金额,单位为分,只能为整数*/

//          data.put("notify_url", Constants.NOTIFY_URL_REFUND);//退款成功后的回调地址
            String preStr = PayCommonUtil.createLinkString(data); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
            //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
            String mySign = PayCommonUtil.sign(preStr, setWechatDO.getKey(), "utf-8").toUpperCase();
            data.put("sign", mySign);

            //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
            String xmlStr = postData("https://api.mch.weixin.qq.com/secapi/pay/refund", PayCommonUtil.GetMapToXML(data),setWechatDO); //支付结果通知的xml格式数据
            System.out.println(xmlStr);
            Map notifyMap = PayCommonUtil.doXMLParse(xmlStr);
            if ("SUCCESS".equals(notifyMap.get("return_code"))) {
                if("SUCCESS".equals(notifyMap.get("result_code"))) {
                    //退款成功的操作
                    OrderOrderDO orderOrderDO = new OrderOrderDO();
                    orderOrderDO.setId(orderId);
                    orderOrderDO.setStatus(6);
                    orderOrderService2.backOrder(orderOrderDO);
                    return R.ok("退款成功");
                }else{
                    System.out.println("退款失败:原因"+notifyMap.get("err_code_des"));
                    return R.error(1,(String)notifyMap.get("err_code_des"));
                }
            }else{
                System.out.println("退款失败:原因"+notifyMap.get("return_msg"));
                return R.error(2,(String)notifyMap.get("return_msg"));
            }
        }catch (Exception e) {
            e.printStackTrace();
            return R.error("系统错误");
        }
    }

    /**
     * 加载证书
     *
     */
    private static void initCert(SetWechatDO setWechatDO) throws Exception {
        // 证书密码，默认为商户ID
        String key = setWechatDO.getMchId();
        // 商户证书的路径, 读取本机存放的PKCS12证书文件
        /*String path = "/api/wxCert.p12";    这个写绝对路径
        FileInputStream instream = new FileInputStream(new File(path));*/
        InputStream instream = PropertiesUtil.class.getResourceAsStream("/public/api/apiclient_cert.p12");

        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();

        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     * 通过Https往API post xml数据
     * @param url  API地址
     * @param xmlObj   要提交的XML数据对象
     * @return
     */
    public String postData(String url, String xmlObj,SetWechatDO setWechatDO) {
        // 加载证书
        try {
            initCert(setWechatDO);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }

    private  String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }



}
